﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using RSuite.Common;
using ServiceStack.Text;

namespace RSuite.R4
{
    public class Package
    {
        public string Filename
        {
            get;
            set;
        }

        public string Input
        {
            get;
            set;
        }

        public int CacheItems()
        {
            this.allItems.Clear();
            if (Directory.Exists(this.Input))
            {
                this.CacheItems(this.Input);
            }
            return this.allItems.Count;
        }

        private void CacheItems(string path)
        {
            var dirInfo = new DirectoryInfo(path);
            foreach (var file in dirInfo.GetFiles())
            {
                if (this.IsTarget(file.Name, this.Excludes, this.Includes))
                {
                    this.allItems.Add(new PackageItem(PackageItem.FILE, file.FullName.Substring(this.Input.Length)));
                }
            }
            foreach (var folder in dirInfo.GetDirectories())
            {
                if (this.IsTarget(folder.Name, this.Excludes, this.Includes))
                {
                    this.allItems.Add(new PackageItem(PackageItem.FOLDER, folder.FullName.Substring(this.Input.Length)));
                    this.CacheItems(folder.FullName);
                }
            }
        }

        private bool IsTarget(string name, string excludes, string includes)
        {
            var excludeList = excludes.Split(new char[] { ' ', ';', ',' }, StringSplitOptions.RemoveEmptyEntries);
            var includeList = includes.Split(new char[] { ' ', ';', ',' }, StringSplitOptions.RemoveEmptyEntries);

            if (excludeList.Length > 0)
            {
                foreach (var item in excludeList)
                {
                    if (StringUtil.MatchFilePattern(name, item.Trim()))
                    {
                        return false;
                    }
                }
            }
            if (includeList.Length > 0)
            {
                foreach (var item in includeList)
                {
                    if (StringUtil.MatchFilePattern(name, item.Trim()))
                    {
                        return true;
                    }
                }
                return false;
            }

            return true;
        }

        public string Output
        {
            get;
            set;
        }

        public string Zip
        {
            get;
            set;
        }

        public string Includes
        {
            get;
            set;
        }

        public string Excludes
        {
            get;
            set;
        }

        public List<PackageItem> Items
        {
            get;
            set;
        }

        private List<PackageItem> allItems = new List<PackageItem>();

        public Package()
        {
            this.Filename = string.Empty;
            this.Input = string.Empty;
            this.Output = string.Empty;
            this.Zip = string.Empty;
            this.Includes = string.Empty;
            this.Excludes = string.Empty;
            this.Items = new List<PackageItem>();
        }

        public void Save()
        {
            this.Save(this.Filename);
        }

        public void Save(string filename)
        {
            this.Filename = filename;
            File.WriteAllText(filename, JsonSerializer.SerializeToString(this));
        }

        public static Package FromFile(string filename)
        {
            return JsonSerializer.DeserializeFromString<Package>(File.ReadAllText(filename));
        }

        public object Search(string input, int limit)
        {
            if (this.Input.StartsWith(input, StringComparison.InvariantCultureIgnoreCase))
            {
                if (File.Exists(input))
                {
                    return new
                    {
                        count = 1,
                        data = new PackageItem[] { new PackageItem(PackageItem.FILE, input.Substring(this.Input.Length)) }
                    };
                }
                else if (Directory.Exists(input))
                {
                    return new
                    {
                        count = 1,
                        data = new PackageItem[] { new PackageItem(PackageItem.FOLDER, input.Substring(this.Input.Length)) }
                    };
                }
                else
                {
                    return new
                    {
                        count = 0,
                        data = new PackageItem[] { }
                    };
                }
            }
            else
            {
                var items = new List<PackageItem>(this.allItems).Where(item => item.HasPattern(input));
                return new
                {
                    count = items.Count(),
                    data = items.Take(limit)
                };
            }
        }

        public void Update(dynamic data)
        {
            this.Input = data["Input"];
            this.Output = data["Output"];
            this.Zip = data["Zip"];
            this.Excludes = data["Excludes"];
            this.Includes = data["Includes"];
            this.Items.Clear();
            for (var i = 0; i < data["Items"].Count; i++)
            {
                this.Items.Add(new PackageItem(int.Parse(data["Items"][i]["Type"]), data["Items"][i]["Path"]));
            }
        }

        public object Pack()
        {
            var items = this.Preview();

            if (string.IsNullOrEmpty(this.Zip))
            {
                this.PackTo(this.Output, items);
            }
            else
            {
                var tempFolder = PathUtil.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
                this.PackTo(tempFolder, items);
                ZipUtil.ZipFolder(tempFolder, PathUtil.Combine(this.Output, this.Zip));
            }

            return new
            {
                status = 1,
                message = ""
            };
        }

        private void PackTo(string path, PackageItem[] items)
        {
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            foreach (var item in items)
            {
                switch (item.Type)
                {
                    case PackageItem.FOLDER:
                        Directory.CreateDirectory(PathUtil.Combine(path, item.Path));
                        break;
                    case PackageItem.FILE:
                        File.Copy(PathUtil.Combine(this.Input, item.Path), PathUtil.Combine(path, item.Path), true);
                        break;
                }
            }
        }

        public PackageItem[] Preview()
        {
            var items = new List<PackageItem>();
            foreach (var item in this.Items)
            {
                items.Add(item);
                if (item.Type == PackageItem.FOLDER)
                {
                    this.ListItems(items, PathUtil.Combine(this.Input, item.Path),
                        string.Format("{0};{1}", this.Excludes, item.Excludes),
                        string.Format("{0};{1}", this.Includes, item.Includes));
                }
            }
            return items.ToArray();
        }

        private void ListItems(List<PackageItem> items, string path, string excludes, string includes)
        {
            var dirInfo = new DirectoryInfo(path);
            foreach (var dir in dirInfo.GetDirectories())
            {
                if (this.IsTarget(dir.Name, excludes, includes))
                {
                    items.Add(new PackageItem(PackageItem.FOLDER, this.GetRelativePath(dir.FullName)));
                    this.ListItems(items, dir.FullName, excludes, includes);
                }
            }
            foreach (var file in dirInfo.GetFiles())
            {
                if (this.IsTarget(file.Name, excludes, includes))
                {
                    items.Add(new PackageItem(PackageItem.FILE, this.GetRelativePath(file.FullName)));
                }
            }
        }

        private string GetRelativePath(string fullpath)
        {
            return fullpath.Substring(this.Input.Length).TrimStart('\\');
        }
    }

    public class PackageItem
    {
        public const int FILE = 1;
        public const int FOLDER = 0;

        public int Type { get; set; }
        public string Path { get; set; }
        public string Includes { get; set; }
        public string Excludes { get; set; }

        public string Name
        {
            get
            {
                return this.Path.Substring(this.Path.LastIndexOf(@"\") + 1);
            }
        }

        public PackageItem(int type, string path)
        {
            this.Type = type;
            this.Path = path;
        }

        public bool HasPattern(string pattern)
        {
            return StringUtil.MatchFilePattern(this.Name, pattern);
        }
    }
}